package com.weiiew.common.utils;

import lombok.Data;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @Description 用于好看的输出元素, 建议仅在调试时使用
 * @Author 魏锴
 * @Date 2024/2/28
 */
@Data
public class BeautifyPrint {
    private static String gap = "\t";

    /**
     * 直接判断8大基本类型和它们的包装类
     * 包含String和Boolean的特殊处理
     * 去除了反射判断更安全可靠
     *
     * @param cls Class类
     * @return 判断结果
     */
    private static boolean isPrimitive(Class<?> cls) {
        return cls.isPrimitive() ||
                cls == String.class ||
                Number.class.isAssignableFrom(cls) ||
                cls == Boolean.class ||
                cls == Character.class;
    }

    /**
     * 输出对象类信息
     * 外界直接调用
     *
     * @param obj 对象
     */
    // 修改print方法
    public static void print(Object obj) {
        try {
            StringBuilder sb = new StringBuilder();
            int type = judgeType(obj);
            dealObj(type, "", obj, sb, new IdentityHashMap<>());
            System.out.println(sb);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static String printString(Object obj){
        try {
            StringBuilder sb = new StringBuilder();
            int type = judgeType(obj);
            dealObj(type, "", obj, sb, new IdentityHashMap<>());
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 普通的处理流程
     *
     * @param prefix 前置
     * @param obj    Object
     * @param sb     字符串
     */

    private static void dealObj(int type, String prefix, Object obj, StringBuilder sb, Map<Object, Boolean> visited) throws InvocationTargetException, IllegalAccessException {
        if (type == 0) sb.append("null");
        else if (type == 1) scanPrimitive(obj, sb, visited);
        else if (type == 2) scanList(prefix + gap, (List<?>) obj, sb, visited);
        else if (type == 3) scanMap(prefix + gap, (Map<?, ?>) obj, sb, visited);
        else if (type == 4) scanArray(prefix + gap, obj, sb, visited);
        else if (type == 5) scanSet(prefix + gap, (Set<?>) obj, sb, visited);  // 新增Set处理分支
        else if (type == -1) scanClass(prefix + gap, obj, sb, visited);
    }

    /**
     * 判断种类
     *
     * @param obj 判断的对象
     * @return 判断结果
     * @throws InvocationTargetException 反射异常
     * @throws IllegalAccessException    当应用程序尝试反射创建实例（数组除外），设置或获取字段或调用方法时，
     *                                   抛出IllegalAccessException，但当前正在执行的方法无法访问指定类的字段，方法或构造函数。
     */

    private static int judgeType(Object obj) throws InvocationTargetException, IllegalAccessException {
        if (obj == null) return 0;
        else if (isPrimitive(obj.getClass())) return 1;
        else if (obj instanceof List<?>) return 2;
        else if (obj instanceof Map<?, ?>) return 3;
        else if (obj instanceof Set<?>) return 5;  // 新增Set类型判断
        else if (obj.getClass().isArray()) return 4;
        else return -1;
    }

    /**
     * 扫描简单元素
     *
     * @param obj     object
     * @param sb      字符串
     * @param visited 已扫描对象
     */
    private static void scanPrimitive(Object obj, StringBuilder sb, Map<Object, Boolean> visited) {
        sb.append(obj.getClass().getSimpleName()).append(" : ").append(obj);
    }

    /**
     * 扫描映射
     *
     * @param prefix  前置
     * @param map     映射信息
     * @param sb      字符流
     * @param visited
     */
    private static void scanMap(String prefix, Map<?, ?> map, StringBuilder sb, Map<Object, Boolean> visited)
            throws InvocationTargetException, IllegalAccessException {
        if (visited.putIfAbsent(map, true) != null) {
            sb.append("Map[Circular Reference]");
            return;
        }
        sb.append("Map: {");
        if (!map.isEmpty()) {
            for (Object key : map.keySet()) {
                sb.append("\n").append(prefix).append(key.getClass().getSimpleName()).append(" : ").append(key).append(" -> ");
                int type = judgeType(map.get(key));
                dealObj(type, prefix, map.get(key), sb, visited);
                sb.append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            sb.append("\n").append(prefix);
            sb.deleteCharAt(sb.length() - 1);
        }
        sb.append("}");
    }

    /**
     * 扫描数组
     *
     * @param array   object[]
     * @param sb      字符串
     * @param visited
     */
    private static void scanArray(String prefix, Object array, StringBuilder sb, Map<Object, Boolean> visited) throws InvocationTargetException, IllegalAccessException {
        if (visited.putIfAbsent(array, true) != null) {
            sb.append("Array: [Circular Reference]");
            return;
        }
        sb.append("Array: [");
        int length = Array.getLength(array);
        if (length > 0) {
            if (length > 1) {
                for (int i = 0; i < length; i++) {
                    Object o = Array.get(array, i);
                    sb.append("\n").append(prefix);
                    dealObj(judgeType(o), prefix, o, sb, visited);
                    sb.append(",");
                }
                sb.deleteCharAt(sb.length() - 1);
                sb.append("\n").append(prefix);
                sb.deleteCharAt(sb.length() - 1);
            } else dealObj(judgeType(Array.get(array, 0)), prefix, Array.get(array, 0), sb, visited);
        }
        sb.append("]");
    }

    /**
     * 扫描集合（新增方法）
     *
     * @param prefix  前置缩进
     * @param set     集合对象
     * @param sb      字符串构建器
     * @param visited
     */
    private static void scanSet(String prefix, Set<?> set, StringBuilder sb, Map<Object, Boolean> visited)
            throws InvocationTargetException, IllegalAccessException {

        if (visited.putIfAbsent(set, true) != null) {
            sb.append("Set: [Circular Reference]");
            return;
        }
        sb.append("Set: {");
        if (!set.isEmpty()) {
            for (Object o : set) {
                sb.append("\n").append(prefix);
                dealObj(judgeType(o), prefix, o, sb, visited);
                sb.append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            sb.append("\n").append(prefix, 0, prefix.length() - gap.length());
        }
        sb.append("}");
    }

    /**
     * 扫描列表
     *
     * @param prefix  前置
     * @param list    列表信息
     * @param sb      字符流
     * @param visited
     */
    private static void scanList(String prefix, List<?> list, StringBuilder sb, Map<Object, Boolean> visited)
            throws InvocationTargetException, IllegalAccessException {
        if (visited.putIfAbsent(list, true) != null) {
            sb.append("List: [Circular Reference]");
            return;
        }
        sb.append("List: [");
        if (!list.isEmpty()) {
            if (list.size() > 1) {
                for (Object o : list) {
                    sb.append("\n").append(prefix);
                    dealObj(judgeType(o), prefix, o, sb, visited);
                    sb.append(",");
                }
                sb.deleteCharAt(sb.length() - 1);
                sb.append("\n").append(prefix);
                sb.deleteCharAt(sb.length() - 1);
            } else dealObj(judgeType(list.get(0)), prefix, list.get(0), sb, visited);
        }
        sb.append("]");
    }

    /**
     * 扫描自定义类
     *
     * @param prefix  前置
     * @param obj     列表信息
     * @param sb      字符流
     * @param visited
     */
    private static void scanClass(String prefix, Object obj, StringBuilder sb, Map<Object, Boolean> visited)
            throws InvocationTargetException, IllegalAccessException {

        if (visited.putIfAbsent(obj, true) != null) {
            sb.append("[Circular Reference]");
            return;
        }

        Class<?> clazz = obj.getClass();
        boolean hasTurn = false;
        sb.append(clazz.getSimpleName()).append("(");
        List<Field> fieldList = new ArrayList<>();
        Class<?> c = clazz;

        while (c != null) {
            fieldList.addAll(Arrays.asList(c.getDeclaredFields()));
            c = c.getSuperclass();
        }

        Field[] fields = fieldList.toArray(new Field[0]);
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            String name = field.getName();
            String firstLetter = name.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + name.substring(1);

            try {
                Method method = obj.getClass().getMethod(getter);
                Object value = method.invoke(obj);
                Class<?> fieldType = field.getType();
                String typeName = fieldType.getSimpleName();

                // 统一字段类型显示格式
                String typePrefix = typeName + " : " + name + " = ";

                if (value == null) {
                    sb.append(typePrefix).append("null");
                } else {
                    int type = judgeType(value);
                    if (type == 1) {  // 基础类型
                        sb.append(typePrefix).append(value);
                    } else {  // 复杂类型
                        hasTurn = true;
                        sb.append("\n").append(prefix).append(typePrefix);
                        dealObj(type, prefix, value, sb, visited);
                    }
                }

                if (i != fields.length - 1) sb.append(", ");
            } catch (NoSuchMethodException ignored) {
            }
        }

        if (hasTurn) {
            sb.append("\n").append(prefix);
            sb.deleteCharAt(sb.length() - 1);
        }
        sb.append(")");
    }
}
